/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pintor;

/**
 *
 * @author Jhon
 */
import java.util.ArrayList;
import processing.core.*;
import remixlab.proscene.*;

public class AlgoritmoPintor extends PApplet {

    Scene scene;
    //arreglo de superficies
    public ArrayList<Superficie> superficies = new ArrayList<Superficie>();
    //boolean q permite hacer o no hacer el algoritmo del pintor
    public boolean pintor = true;

    public void setup() {
        size(800, 600, P3D);
        scene = new Scene(this);


        hint(DISABLE_DEPTH_TEST);

    }

    public void draw() {
        //borra el fondo
        background(0, 0, 0);
        //el algoritmo del pintor
        if (pintor) {
            CalcularVertices();
            OrdenarSuperficies();
            Restricciones();
        }
    }

    //aplica las pruebas del algoritmo del pintor una vez ordenado las superficies
    public void Restricciones() {
        Superficie aux;
        boolean prueba;
        ArrayList<Superficie> supe = new ArrayList<Superficie>();
        supe.addAll(superficies);





        if (superficies.size() >= 2) { //mira ssi hay máss de dos superficies
            int i = 0;
            int j = 1;

            while (supe.size() >= 1) {
                //si solo hay un elemento en la lista lo pinta
                if (supe.size() == 1) {
                    Pintar(supe.get(i));
                    break;
                }

                prueba = false;
                //Restriccion 1 no se translapa en X
                if (supe.get(i).xmax <= supe.get(j).xmin) {
                    prueba = true;
                    //Restriccion 1 no se translapa en Y
                } else if (supe.get(i).ymax <= supe.get(j).ymin) {
                    prueba = true;
                    //S está detrás del plano de S'
                } else if (supe.get(j).a * supe.get(i).puntos.get(0).getX()
                        + supe.get(j).b * supe.get(i).puntos.get(0).getY()
                        + supe.get(j).c * supe.get(i).puntos.get(0).getZ()
                        + supe.get(j).d <= 0
                        && supe.get(j).a * supe.get(i).puntos.get(1).getX()
                        + supe.get(j).b * supe.get(i).puntos.get(1).getY()
                        + supe.get(j).c * supe.get(i).puntos.get(1).getZ()
                        + supe.get(j).d <= 0
                        && supe.get(j).a * supe.get(i).puntos.get(2).getX()
                        + supe.get(j).b * supe.get(i).puntos.get(2).getY()
                        + supe.get(j).c * supe.get(i).puntos.get(2).getZ()
                        + supe.get(j).d <= 0) {
                    prueba = true;
                } //S' está delante del plano de S
                else if (supe.get(i).a * supe.get(j).puntos.get(0).getX()
                        + supe.get(i).b * supe.get(j).puntos.get(0).getY()
                        + supe.get(i).c * supe.get(j).puntos.get(0).getZ()
                        + supe.get(i).d >= 0
                        && supe.get(i).a * supe.get(j).puntos.get(1).getX()
                        + supe.get(i).b * supe.get(j).puntos.get(1).getY()
                        + supe.get(i).c * supe.get(j).puntos.get(1).getZ()
                        + supe.get(i).d >= 0
                        && supe.get(i).a * supe.get(j).puntos.get(2).getX()
                        + supe.get(i).b * supe.get(j).puntos.get(2).getY()
                        + supe.get(i).c * supe.get(j).puntos.get(2).getZ()
                        + supe.get(i).d >= 0) {
                    prueba = true;
                }



                // si la prueba sale positiva pinta:
                if (prueba) {
                    //y si la comparo con el ultimo de la lista.... pinta ese poligono
                    //lo remueve de la lista y empieza otra vez a comparar
                    if (j == supe.size() - 1) {
                        Pintar(supe.get(i));
                        supe.remove(i);
                        j = 1;
                    //sino sigue comparando....
                    } else {
                        j++;
                    }


                } else {
                    //verifica si ya cambio ese poligono de la lista

                    if (!supe.get(j).cambio) {
                        // le hace el swap
                        aux = supe.get(i);
                        supe.set(i, supe.get(j));
                        supe.set(j, aux);
                        supe.get(i).cambio = true;
                        //y vuelve hacer la comparacion
                        j = 1;

                    } else {
                        //si ya lo cambio y llego al final de la lista pinta ese poligono
                        if (j == supe.size() - 1) {
                            Pintar(supe.get(i));
                            supe.remove(i);
                            j = 1;
                            //sino sigue comparando con el siguiente
                        } else {
                            j++;
                        }

                    }

                }

            }




        }

    }
    //simplemente me calcula para cada superficie los xmax, ymax ....

    public void CalcularVertices() {
        for (Superficie sup : superficies) {
            Puntos a1 = getPuntoCamara(sup.getPuntos().get(0));
            Puntos a2 = getPuntoCamara(sup.getPuntos().get(1));
            Puntos a3 = getPuntoCamara(sup.getPuntos().get(2));
            sup.xmax = Math.abs(maxPuntos(a1, a2, a3, 0));
            sup.xmin = Math.abs(minPuntos(a1, a2, a3, 0));
            sup.ymax = Math.abs(maxPuntos(a1, a2, a3, 1));
            sup.ymin = Math.abs(minPuntos(a1, a2, a3, 1));
            sup.zmax = Math.abs(maxPuntos(a1, a2, a3, 2));
            sup.zmin = Math.abs(minPuntos(a1, a2, a3, 2));

        }
    }

    //metodo q pinta superfice por superficie
    public void Pintar() {
        for (Superficie sup : superficies) {
            beginShape();
            fill(sup.R, sup.G, sup.B);
            for (Puntos pu : sup.getPuntos()) {

                vertex(pu.getX(), pu.getY(), pu.getZ());

            }
            endShape();

        }

    }

    public void Pintar(Superficie sup) {

        beginShape();
        fill(sup.R, sup.G, sup.B);
        for (Puntos pu : sup.getPuntos()) {

            vertex(pu.getX(), pu.getY(), pu.getZ());

        }
        endShape();



    }

    public void keyPressed() {

        //me agrega 2 superficies que comparten un vertice
        if (key == 'p') {
            //primera superfice
            Puntos p1 = new Puntos((int) scene.camera().position().x, (int) scene.camera().position().y, (int) scene.camera().position().z);
            Puntos p2 = new Puntos(p1.getX() + (int) (Math.random() * 100), p1.getY() + (int) (Math.random() * 80), p1.getZ() + (int) (Math.random() * 120));
            Puntos p3 = new Puntos(p2.getX() + (int) (Math.random() * 80), p2.getY() - (int) (Math.random() * 140), p2.getZ() + (int) (Math.random() * 80));

            //me crea la superficie con los puntos y los colores R,G,B
            superficies.add(new Superficie(p1, p2, p3, (int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256)));


            p3 = new Puntos(p2.getX() - (int) (Math.random() * 40), p2.getY() + (int) (Math.random() * 80), p2.getZ() - (int) (Math.random() * 140));
            //me crea la segunda superficie con los puntos y los colores R,G,B
            superficies.add(new Superficie(p1, p2, p3, (int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256)));
        } else if (key == 'o') {
            pintor = !pintor;
            if (pintor) {
                System.out.println("PINTOR ON");
            } else {
                System.out.println("PINTOR OFF");
            }
        }
    }

    //me transforma los puntos a coordenadas de camara
    public Puntos getPuntoCamara(Puntos p) {
        PVector pV = scene.camera().frame().coordinatesOf(new PVector(p.getX(), p.getY(), p.getZ()));
        return new Puntos((int) pV.x, (int) pV.y, (int) pV.z);
    }

    //me ordena las superficies por zmax
    public void OrdenarSuperficies() {
        Superficie aux;
        for (int i = 0; i < superficies.size(); i++) {
            for (int j = i; j < superficies.size(); j++) {
                if (superficies.get(i).zmax < superficies.get(j).zmax) {
                    aux = superficies.get(i);
                    superficies.set(i, superficies.get(j));
                    superficies.set(j, aux);
                }

            }

        }

    }

    //me devuelve el minimo, dependiendo del valor u, si u=0 me devuelve el xmin, si u=1 me devuelve ymin....
    public int minPuntos(Puntos p1, Puntos p2, Puntos p3, int u) {
        if (u == 0) {
            if (p1.x <= p2.x && p1.x <= p3.x) {
                return p1.x;
            } else if (p2.x <= p1.x && p2.x <= p3.x) {
                return p2.x;
            } else {
                return p3.x;
            }
        } else if (u == 1) {
            if (p1.y <= p2.y && p1.y <= p3.y) {
                return p1.y;
            } else if (p2.y <= p1.y && p2.y <= p3.y) {
                return p2.y;
            } else {
                return p3.x;
            }
        } else {
            if (p1.z <= p2.z && p1.z <= p3.z) {
                return p1.z;
            } else if (p2.z <= p1.z && p2.z <= p3.z) {
                return p2.z;
            } else {
                return p3.z;
            }
        }
    }

    //me devuelve el maximo, dependiendo del valor u, si u=0 me devuelve el xmas, si u=1 me devuelve ymax....
    public int maxPuntos(Puntos p1, Puntos p2, Puntos p3, int u) {
        if (u == 0) {
            if (p1.x >= p2.x && p1.x >= p3.x) {
                return p1.x;
            } else if (p2.x >= p1.x && p2.x >= p3.x) {
                return p2.x;
            } else {
                return p3.x;
            }
        } else if (u == 1) {
            if (p1.y >= p2.y && p1.y >= p3.y) {
                return p1.y;
            } else if (p2.y >= p1.y && p2.y >= p3.y) {
                return p2.y;
            } else {
                return p3.x;
            }
        } else {
            if (p1.z >= p2.z && p1.z >= p3.z) {
                return p1.z;
            } else if (p2.z >= p1.z && p2.z >= p3.z) {
                return p2.z;
            } else {
                return p3.z;
            }
        }
    }

    public static void main(String args[]) {
        PApplet.main(new String[]{"pintor.AlgoritmoPintor"});
    }
}
